// uecgi_http.cpp
#include <windows.h>

// UELogger Support
#include "litelog.h"
extern "C" extern int logfd;

// API declaration
#include "uecgi_http.h"

CUECGIHttp::CUECGIHttp()
{
	m_bCookieParsed = FALSE;

	m_pBody = NULL;
	m_nBodyLen = 0;
	m_bBodyRetrieved = FALSE;
}

CUECGIHttp::~CUECGIHttp()
{
	if (m_nBodyLen > 0)
		ClearBody();
}

BOOL CUECGIHttp::AddQuery(char *strQuery)
{
	BOOL bResult = TRUE;
	char *p, ch = 0;

	dbglogex("Enter CUECGIHttp::AddQuery(%s)", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, strQuery);

	// parameter check
	if (strQuery && strlen(strQuery) > 0)
	{
		// parse the query name and value
		p = strQuery;
		while (*p != '=' && *p != 0)
			p++;

		if (*p != 0)
		{
			ch = *p;
			*p = 0;
			++p;
		}
		dbglogex("New query name: \"%s\", value: \"%s\"", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, strQuery, p);

		// append the new item into query list
		if(m_Queries.insert(std::map<std::string, std::string>::value_type(strQuery, p)).second)
		{
			dbglog("Append the new query item success", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
		}
		else
		{
			dbglog("Append the new query item failed, possiblly the query item with same name already added before", DEBUG_LOG_LEVEL_WARN, logfd, __FILE__, __LINE__);
		}

		if (ch)
		{
			--p;
			*p = ch;
		}
		
	} // parameter check success
	else
	{
		dbglog("Invalid parameter, the input strQuery is empty", DEBUG_LOG_LEVEL_WARN, logfd, __FILE__, __LINE__);
	} // parameter check failed	

	dbglog("Exit CUECGIHttp::AddQuery", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	return bResult;
}

BOOL CUECGIHttp::ParseQueries(char *strQueries)
{
	BOOL bResult = TRUE;
	char *p, *q, ch;
	const char delimits[] = "&";

	dbglogex("Enter CUECGIHttp::ParseQueries(%s)", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, strQueries);

	if (strQueries && strlen(strQueries) > 0)
	{
		p = strQueries;

		// loop to skip all the space at the beginning
		while (*p && *p == ' ')
			p++;

		// loop to parse all the query paramters 
		while (*p)
		{
			q = p;
			while (*q && !strchr(delimits, *q))
				q++;

			if (*q == 0)
			{
				bResult = AddQuery(p);

				p = q;
			} // reach the end of string
			else
			{
				ch = *q;
				*q = 0;
				bResult = AddQuery(p);
				*q = ch;

				p = ++q;
			} // not reach the end of string

			// loop to skip all the space at the beginning
			while (*p && *p == ' ')
				p++;

			if (!bResult)
			{
				dbglog("Add new query item failed.", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
				break;
			}
		} // loop to parse all the cookies
	} // parameter check success
	else
	{
		dbglog("Invalid parameter, the input strQueries is empty", DEBUG_LOG_LEVEL_WARN, logfd, __FILE__, __LINE__);
	} // parameter check failed

	dbglog("Exit CUECGIHttp::ParseQueries", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	return bResult;
}

const char* CUECGIHttp::GetQuery(const char *strQueryName)
{
	const char *strResult = NULL;

	dbglogex("Enter CUECGIHttp::GetQuery(%s)", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, strQueryName);

	// parameter check
	if (strQueryName && strlen(strQueryName) > 0)
	{
		std::map <std::string, std::string>::const_iterator query;
		if ((query = m_Queries.find(strQueryName)) != m_Queries.end())
		{
			strResult = query->second.c_str();
			dbglogex("Query parameter of specified name found, the value is: %s", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, strResult);
		}
		else
		{
			dbglog("Can't found the query parameter of specified name", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
		}
	} // parameter check success
	else
	{
		dbglog("Invalid parameter, the input query name is empty", DEBUG_LOG_LEVEL_WARN, logfd, __FILE__, __LINE__);
	} // parameter check failed

	dbglog("Exit CUECGIHttp::GetQuery", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	return strResult;
}

BOOL CUECGIHttp::AddCookie(char *strCookie)
{
	BOOL bResult = TRUE;
	char *p, ch = 0;

	dbglogex("Enter CUECGIHttp::AddCookie(%s)", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, strCookie);

	// parameter check
	if (strCookie && strlen(strCookie) > 0)
	{
		// parse the cookie name and value
		p = strCookie;
		while (*p != '=' && *p != 0)
			p++;

		if (*p != 0)
		{
			ch = *p;
			*p = 0;
			++p;
		}
		dbglogex("New cookie name: \"%s\", value: \"%s\"", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, strCookie, p);

		// append the new item into cookie list
		if(m_Cookies.insert(std::map<std::string, std::string>::value_type(strCookie, p)).second)
		{
			dbglog("Append the new cookie success", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
		}
		else
		{
			dbglog("Append the new cookie failed, possiblly the cookie already added before", DEBUG_LOG_LEVEL_WARN, logfd, __FILE__, __LINE__);
		}

		if (ch)
		{
			--p;
			*p = ch;
		}
		
	} // parameter check success
	else
	{
		dbglog("Invalid parameter, the input strCookie is empty", DEBUG_LOG_LEVEL_WARN, logfd, __FILE__, __LINE__);
	} // parameter check failed	

	dbglog("Exit CUECGIHttp::AddCookie", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	return bResult;
}

BOOL CUECGIHttp::ParseCookies(char *strCookies)
{
	BOOL bResult = TRUE;
	char *p, *q, ch;
	const char delimits[] = ";";

	dbglogex("Enter CUECGIHttp::ParseCookies(%s)", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, strCookies);

	if (strCookies && strlen(strCookies) > 0)
	{
		p = strCookies;

		// loop to skip all the space at the beginning
		while (*p && *p == ' ')
			p++;

		// loop to parse all the query paramters 
		while (*p)
		{
			q = p;
			while (*q && !strchr(delimits, *q))
				q++;

			if (*q == 0)
			{
				bResult = AddCookie(p);

				p = q;
			} // reach the end of string
			else
			{
				ch = *q;
				*q = 0;
				bResult = AddCookie(p);
				*q = ch;

				p = ++q;
			} // not reach the end of string

			// loop to skip all the space at the beginning
			while (*p && *p == ' ')
				p++;

			if (!bResult)
			{
				dbglog("Add new cookie failed.", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
				break;
			}
		} // loop to parse all the cookies
	} // parameter check success
	else
	{
		dbglog("Invalid parameter, the input strCookies is empty", DEBUG_LOG_LEVEL_WARN, logfd, __FILE__, __LINE__);
	} // parameter check failed

	// make m_Cookies valid
	m_bCookieParsed = TRUE;

	dbglog("Exit CUECGIHttp::ParseCookies", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	return bResult;
}

BOOL CUECGIHttp::RetrieveCookies()
{
	BOOL bResult = TRUE;
	char *strenv = NULL;
	unsigned int nLen;

	dbglog("Enter CUECGIHttp::RetrieveCookies()", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	// Clear old cookies if any
	ClearCookies();

	// Get cookies string from envrionment variables
	int err = _dupenv_s(&strenv, &nLen, "HTTP_COOKIE");
	if (!err)
	{
		dbglogex("Get HTTP_COOKIE environment variable success: %s", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, strenv);

		bResult = ParseCookies(strenv);

		free(strenv);
	} // Get cookies environment variable failed
	else
	{
		dbglogex("Get HTTP_COOKIE environment variable failed. Error Code: %d", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__, err);
		bResult = FALSE;
	} // Get cookies environment variable failed

	dbglog("Exit CUECGIHttp::RetrieveCookies", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	return bResult;
}

BOOL CUECGIHttp::RetrieveBody()
{
	BOOL bResult = TRUE;
	char *strenv = NULL;
	unsigned int nLen, nReadLen;

	dbglog("Enter CUECGIHttp::RetrieveBody()", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	// Clear old body if any
	ClearBody();

	// Get body length string from envrionment variables
	int err = _dupenv_s(&strenv, &nLen, "CONTENT_LENGTH");
	if (!err)
	{
		dbglogex("Get CONTENT_LENGTH environment variable success: %s", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, strenv);

		m_nBodyLen = atoi(strenv);
		free(strenv);

		if (m_nBodyLen > 0)
		{
			dbglogex("CONTENT_LENGTH is %d.", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, m_nBodyLen);
			m_pBody = malloc(m_nBodyLen + 2);
			if (m_pBody)
			{
				dbglog("Malloc memory for read body success", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
				nLen = 0;
				while((int)nLen < m_nBodyLen)
				{
					nReadLen = fread((void*)((char*)m_pBody + nLen), 1, m_nBodyLen - nLen, stdin);
					nLen += nReadLen;

					dbglogex("Read %d bytes, total (%d/%d)", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, nReadLen, nLen, m_nBodyLen);
					if (nReadLen == 0)
						break;
				}

				if ((int)nLen >= m_nBodyLen)
				{
					dbglog("Read body complete", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
					m_bBodyRetrieved = TRUE;

					// always add two bytes 0 at the end of body for string value
					((char*)m_pBody)[m_nBodyLen] = 0;
					((char*)m_pBody)[m_nBodyLen + 1] = 0;
				} // read body complete
				else
				{
					dbglog("Read body incomplete", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
					free(m_pBody);
					m_pBody = NULL;
					m_nBodyLen = 0;
					bResult = FALSE;
				} // read body incomplete
			} // malloc memory for body success
			else
			{
				dbglog("Malloc memory for read body failed", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
				m_nBodyLen = 0;
				bResult = FALSE;
			} // malloc memory for body failed
		} // has body
		else
		{
			dbglog("CONTENT_LENGTH is 0, no body.", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

			m_pBody = NULL;
			m_bBodyRetrieved = TRUE;
		} // no body
	} // Get cookies environment variable failed
	else
	{
		dbglogex("Get CONTENT_LENGTH environment variable failed. Error Code: %d", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__, err);
		bResult = FALSE;
	} // Get cookies environment variable failed

	dbglog("Exit CUECGIHttp::RetrieveBody", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	return bResult;
}

void CUECGIHttp::ClearCookies()
{
	dbglog("Enter CUECGIHttp::ClearCookies()", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	m_bCookieParsed = FALSE;
	m_Cookies.clear();

	dbglog("Exit CUECGIHttp::ClearCookies", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
}

void CUECGIHttp::ClearBody()
{
	dbglog("Enter CUECGIHttp::ClearBody()", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	m_bBodyRetrieved = FALSE;
	if (m_pBody)
	{
		free(m_pBody);
		m_pBody = NULL;
	}
	m_nBodyLen = 0;

	dbglog("Exit CUECGIHttp::ClearBody", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
}

const char* CUECGIHttp::GetCookie(const char *strCookieName)
{
	const char *strResult = NULL;

	dbglogex("Enter CUECGIHttp::GetCookie(%s)", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, strCookieName);

	// instance status check
	if (m_bCookieParsed)
	{
		// parameter check
		if (strCookieName && strlen(strCookieName) > 0)
		{
			std::map <std::string, std::string>::const_iterator cookie;
			if ((cookie = m_Cookies.find(strCookieName)) != m_Cookies.end())
			{
				strResult = cookie->second.c_str();
				dbglogex("Cookie of specified name found, the value is: %s", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, strResult);
			}
			else
			{
				dbglog("Can't found the cookie of specified name", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
			}
		} // parameter check success
		else
		{
			dbglog("Invalid parameter, the input cookie name is empty", DEBUG_LOG_LEVEL_WARN, logfd, __FILE__, __LINE__);
		} // parameter check failed
	} // status check success
	else
	{
		dbglog("Cookie not be parsed, nothing to return", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
	} // status check failed

	dbglog("Exit CUECGIHttp::GetCookie", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	return strResult;
}

const char* CUECGIHttp::GetSessionID()
{
	const char *strResult = NULL;

	dbglog("Enter CUECGIHttp::GetSessionID()", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	if (m_bCookieParsed)
	{
		dbglog("Cookie already parsed, try to directly find the value", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
		strResult = GetCookie(PARAMETER_SESSIONID_NAME);
	} // cookie already parsed
	else
	{
		dbglog("Cookie not parsed, retrieve cookies and parse them", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
		
		if (RetrieveCookies())
		{
			dbglog("Retrieve and parse cookies success", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
			strResult = GetCookie(PARAMETER_SESSIONID_NAME);
		} // retrieve and parse cookies success
		else
		{
			dbglog("Retrieve and parse cookies failed", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
		} // retrieve and parse cookies failed
	} // cookie not parsed, retrieve cookies and parse them

	dbglog("Exit CUECGIHttp::GetSessionID", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	return strResult;
}

int CUECGIHttp::GetBodyLength()
{
	int nResult = 0;

	dbglog("Enter CUECGIHttp::GetBodyLength()", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	if (m_bBodyRetrieved)
	{
		dbglog("Body already parsed", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
		nResult = m_nBodyLen;
	} // body already parsed
	else
	{
		dbglog("Body not parsed, retrieve body", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
		
		if (RetrieveBody())
		{
			dbglog("Retrieve body success", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
			nResult = m_nBodyLen;
		} // retrieve body success
		else
		{
			dbglog("Retrieve body failed", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
		} // retrieve body failed
	} // body not parsed, retrieve body

	dbglog("Exit CUECGIHttp::GetBodyLength", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	return nResult;
}

void* CUECGIHttp::GetBody()
{
	void* pResult = NULL;

	dbglog("Enter CUECGIHttp::GetBody()", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	if (m_bBodyRetrieved)
	{
		dbglog("Body already parsed", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
		pResult = m_pBody;
	} // body already parsed
	else
	{
		dbglog("Body not parsed, retrieve body", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
		
		if (RetrieveBody())
		{
			dbglog("Retrieve body success", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
			pResult = m_pBody;
		} // retrieve body success
		else
		{
			dbglog("Retrieve body failed", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
		} // retrieve body failed
	} // body not parsed, retrieve body

	dbglog("Exit CUECGIHttp::GetBody", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	return pResult;
}